//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <elatypes.h>
#include <clsinfo.h>
#include <elapi.h>
#include <stdlib.h>
#include <pseudo.h>
#include <aura.h>
#include "locmod.h"
#include <_pubcrt.h>

extern CIClassInfo *g_elasys_classInfo;
//static CIClassInfo *s_elasys_relocated_classInfo = NULL;

extern CriticalSection g_staticObjectListLock;
extern CriticalSection g_processExitRoutineListLock;

EXTERN_C ECode InitCConsoleIo();
EXTERN_C ECode UninitCConsoleIo();

EXTERN_C ECode CDECL PseudoKernel_Initialize();
EXTERN_C void  CDECL PseudoKernel_Destroy();

EXTERN_C ECode CDECL InitChannelPool();
EXTERN_C void  CDECL DestroyChannelPool();

EXTERN ECode InitModListLock();

EXTERN ECode InitSharedClsInfo();
EXTERN void DeleteSharedClsInfo();
EXTERN ECode RegisterSharedClassInfo(CIClassInfo *pClassInfo);

EXTERN_C ECode InitProxyEntry(void);
EXTERN_C void DestroyProxyEntry(void);
EXTERN_C ECode InitService(void);
EXTERN_C ECode DestroyService(void);

EXTERN ECode InitializeROT();
EXTERN void  DestroyROT();

EXTERN void InitTLS();
EXTERN void UninitTLS();

EXTERN void RelocateClassInfo(CIClassInfo* pInClsInfoData,
                        CIClassInfo * pClsInfo);

EXTERN_C IDeviceDriver * CDECL CreateBootModules(uint_t uDeviceNo, void *pvParameter);
IDeviceDriver *g_pBootModulesDrv = NULL;

EXTERN DLinkNode g_LocModList;
EXTERN CriticalSection g_LocModListLock;

EXTERN UInt32 s_uDelayLoopCount;

EXTERN_C _ELASTOS Boolean CDECL DeployEnvironString();

CIClassInfo * s_pKerClassInfo = NULL;

bool g_bUsingConsoleChannel = false;

static
ECode GetLoopCountPerMicrosecond(UInt32 * pLoopCount)
{
    Int64 frequency = 0;
    Int64 startCounter = 0, endCounter = 0;
    Double diffTime;
    UInt32 loopCount = 0;
    ECode ec = NOERROR;

#define TESTCOUNT (100)

    ec = AuraSystem_QueryPerformanceFrequency((Aura_Int64 *)&frequency);
    if (FAILED(ec)) goto E_FAIL_EXIT;
    ec = AuraSystem_QueryPerformanceCounter((Aura_Int64 *)&startCounter);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    for (Int32 i = 0; i < TESTCOUNT; i++);
    ec = AuraSystem_QueryPerformanceCounter((Aura_Int64 *)&endCounter);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    diffTime = ((Double)(endCounter - startCounter) / frequency) * 1000000; // Microseconds.

    loopCount = (UInt32)(TESTCOUNT / diffTime);
    if (!loopCount) {
        loopCount = 1;
    }
    *pLoopCount = loopCount;
    return NOERROR;

E_FAIL_EXIT:
    *pLoopCount = 0;
    return ec;
}

EXTERN_C void InitConsoleChannel()
{
    AStringBuf_<_MAX_PATH> asAppName;
    AStringBuf_<_MAX_PATH> asKeyName;
    AStringBuf_<_MAX_PATH> asReturnString;
    asAppName.Copy("CompoundChannel");
    asKeyName.Copy("ConsoleChannel");
    ECode ec = _CProfile_GetAString(asAppName, asKeyName, &asReturnString);
    if (!FAILED(ec) && asReturnString.Compare("Enable") == 0) {
        g_bUsingConsoleChannel = true;
    }
}

EXTERN_C _ELASTOS Boolean AttachElauraDll()
{
    ECode ec = NOERROR;

    InitChannelPool();

    ec = PseudoKernel_Initialize();
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    if (!DeployEnvironString()) {
        goto ErrorExit;
    }

    ec = GetLoopCountPerMicrosecond(&s_uDelayLoopCount);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    ec = _CCriticalSection_Initialize(&g_staticObjectListLock);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    ec = _CCriticalSection_Initialize(&g_processExitRoutineListLock);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

//    InitModListLock();
    ec = _CCriticalSection_Initialize(&g_LocModListLock);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    InitSharedClsInfo();

    InitTLS();
    InitializeROT();
    InitProxyEntry();

    ec = InitServerClassInfo();
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    s_pKerClassInfo
           = (CIClassInfo *)_CMemoryHeap_Alloc(
                              g_elasys_classInfo->totalSize,
                              FALSE);
    RelocateClassInfo(g_elasys_classInfo, s_pKerClassInfo);
    ec = RegisterServerClassInfo(s_pKerClassInfo);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    g_pBootModulesDrv = CreateBootModules(0, NULL);
    if (NULL == g_pBootModulesDrv) {
        goto ErrorExit;
    }

    ec = InitCConsoleIo();
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    InitConsoleChannel();

    InitService();
    return TRUE;

ErrorExit:
    return FALSE;
}

EXTERN_C void DetachElauraDll()
{
    g_bUsingConsoleChannel = false;

    DestroyService();
    UninitCConsoleIo();

    _CMemoryHeap_Free(s_pKerClassInfo);

    UnregisterServerClassInfos();

    DeleteSharedClsInfo();
    DestroyProxyEntry();
    DestroyChannelPool();

    DestroyROT();

    UninitTLS();
    _CCriticalSection_Terminate(&g_staticObjectListLock);
    _CCriticalSection_Terminate(&g_processExitRoutineListLock);

    DLinkNode * pLocalModule = NULL;

    while (!g_LocModList.IsEmpty()) {
        pLocalModule = g_LocModList.Next();
        pLocalModule->Detach();
        ((LocalModule *)pLocalModule)->m_pIModule->Release();
        delete ((LocalModule *)pLocalModule)->m_pwszUunm;
        delete pLocalModule;
        pLocalModule = NULL;
    }
    _CCriticalSection_Terminate(&g_LocModListLock);

    PseudoKernel_Destroy();

    return ;
}
